public class ONEN_OppGivingRollup {

	/* problem with this is that no triggers on Contact Role, so if you insert an Opp
	*  this will never update a contact, as the contact relationship comes later
	*  so to get it to fire, the opp has to be modified after the contact role is associated
	*  which is fine in daily use, only rare hand-done cases will be missed. But if we're
	*  inserting complete payments, the totals won't update. Kludge would be to insert the opp
	*  create the contact role, then update the opp again
	*  Update: added custom fields on the Opp to hold the ref to Contact role, that way can
	*  handle the insert case, updating the contact's giving total while creating the contact
	*  roles after the insert
	*/

	private static string MbrRectype = ONEN_Constants.OPP_MEMBER_RECTYPE_FORTESTS;
	private static string GiftRectype = ONEN_Constants.OPP_NONMEMBER_RECTYPE_FORTESTS;
	
	public static list<contact> RollupGivingTotalsToContacts ( set<id> OppsToProcess , boolean skipOpps, set<id> nongiftRTids ) {
		 
		// given a list of Opp id's (which need to be pre-tested that they are memberships
		//   and include con role(s)
		// roll up giving totals to contacts in the primary con role
		//
		// if skipOpps is true, do the same but omit the opp ID's actually passed in when
		//	calculating the stats (meaning figure membership stats based on all OTHER member
		//	opps for the household except the ones passed in)
		//	this is for the before delete trigger: we need to lookup the contacts and HH from
		//		the opp being deleted, but don't want to include those opps in the stats
		//		since it's on the way out
		
		set<id> ConIds = new set<id> ();	
		list<contact> ContactsToUpdate = new list<contact> ();
		
		// extract set of contact Id's from the opps
		Opportunity[] OppsWithCR = [Select Id, (SELECT ContactID FROM OpportunityContactRoles WHERE IsPrimary = True LIMIT 1) From Opportunity WHERE Id IN :OppsToProcess ];
		for ( Opportunity thisOpp : OppsWithCR ) {
			if ( thisOpp.OpportunityContactRoles.size() > 0 ) {
				id thisConId = thisOpp.OpportunityContactRoles[0].ContactId;
				ConIds.add (thisConId);
			}
		}

		// now loop thru those contact ID's and create an (empty) OppHistory object for each contact		
		map<id,ONEN_OppHistoryMap> ConsToOpps = new map<id,ONEN_OppHistoryMap> () ;
		for ( id thisConId : ConIds ) {
			ONEN_OppHistoryMap thisOH = new ONEN_OppHistoryMap (thisConId);
			ConsToOpps.put (thisConId,thisOH);
		}
//		system.debug (ConsToOpps);
		
		
		// now find all gift-type opps for these contacts, and add to OppHistory obj in date order		
//		Opportunity[] GiftOpps = [SELECT Name, Id, Amount, CloseDate, (SELECT ContactID FROM OpportunityContactRoles WHERE IsPrimary = True AND ContactID IN :ConIds LIMIT 1) FROM Opportunity WHERE IsWon = true AND RecordTypeId NOT IN :nongiftRTids ORDER BY CloseDate ];		
		OpportunityContactRole[] GiftOppCRs = new List<OpportunityContactRole> ();
		if ( skipOpps ) {
			GiftOppCRs= [SELECT ContactId, Opportunity.Name, Opportunity.Id, Opportunity.Amount, Opportunity.CloseDate FROM OpportunityContactRole WHERE IsPrimary = True AND ContactID IN :ConIds AND Opportunity.IsWon = true AND Opportunity.RecordTypeId NOT IN :nongiftRTids AND Opportunity.ID NOT IN :OppsToProcess ORDER BY Opportunity.CloseDate ];		
		} else {
			GiftOppCRs= [SELECT ContactId, Opportunity.Name, Opportunity.Id, Opportunity.Amount, Opportunity.CloseDate FROM OpportunityContactRole WHERE IsPrimary = True AND ContactID IN :ConIds AND Opportunity.IsWon = true AND Opportunity.RecordTypeId NOT IN :nongiftRTids ORDER BY Opportunity.CloseDate ];		
		}
//		system.debug (GiftOppCRs);
		
		for ( OpportunityContactRole thisOppCR : GiftOppCRs ) {
//			system.debug (thisOpp.OpportunityContactRoles[0].ContactId);
			Opportunity thisOpp = thisOppCR.Opportunity;
			ONEN_OppHistoryMap thisOH = ConsToOpps.get (thisOppCR.ContactId);
			thisOH.AddOpp (thisOpp);	
		}
		
		// now loop through all of our contacts again
		// btw, we do want to write to every contact, even those w/ no found Opps
		// because we may need to write null values for opps deleted or changed ClosedWon -> prospecting, etc.
		for ( id thisConId : ConIds ) {

			// need to declare these up here, so they'll be present but null if we have no opps for ann acct			
			integer maxYear, maxYearMinus1, maxYearMinus2;
			double maxYearAmt, maxYearMinus1Amt, maxYearMinus2Amt;
			double lifetimeAmt;
			
			// pull down the opp history for this contact & look at date of last opp in the chain
			ONEN_OppHistoryMap thisOH = ConsToOpps.get (thisConId);
			Opportunity lastOpp = thisOH.GetLastOpp ();
//			Opportunity firstOpp = thisOH.GetFirstOpp ();

			if ( lastOpp != null ) {
				
				// get totals for the year of the last opp, & the 2 years prior				
				maxYear = lastOpp.CloseDate.year();
				maxYearAmt = thisOH.GetOppTotalForYear(maxYear);
				
				maxYearMinus1 = maxYear - 1;
				maxYearMinus1Amt = thisOH.GetOppTotalForYear(maxYearMinus1);			
	
				maxYearMinus2 = maxYear - 2;
				maxYearMinus2Amt = thisOH.GetOppTotalForYear(maxYearMinus2);			

				// also calc lifetime amt. while we're at it
				lifetimeAmt = thisOH.GetOppTotalAmt();

			}
				
			Contact thisCon = new Contact (id = thisConId);
			//thisAcct.id = ThisAcctId;
			thisCon.Last_CY_Of_Gifts__c = maxYear;
			thisCon.Last_CY_Total_Gifts__c = maxYearAmt;
			thisCon.Last_CY_Less_1_Of_Gifts__c = maxYearMinus1;
			thisCon.Last_CY_Less_1_Total_Gifts__c = maxYearMinus1Amt;
			thisCon.Last_CY_Less_2_Of_Gifts__c = maxYearMinus2;
			thisCon.Last_CY_Less_2_Total_Gifts__c = maxYearMinus2Amt;
			thisCon.Total_Gifts_Lifetime__c = lifetimeAmt;
			
			ContactsToUpdate.add (thisCon);

		}
		
		return ContactsToUpdate;
		
	}	
	
	


}